/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */
/* Copyright (C) 2012 Zongyou Yao. All rights reserved.     */

#ifndef EXCEPT_INCLUDED
#define EXCEPT_INCLUDED

#include <setjmp.h>

typedef struct except_t {
    const char *reason;
} except_t;

typedef struct except_frame except_frame;
struct except_frame {
    except_frame *prev;
    jmp_buf env;
    const char *file;
    int line;
    const except_t *exception;
};

enum {
    except_entered = 0,
    except_raised,
    except_handled,
    except_finalized
};

extern except_frame *except_stack;
extern const except_t assert_failed;
void except_raise(const except_t *e, const char *file, int line);

#ifdef WIN32
#include <windows.h>

extern int except_index;
extern void except_init(void);
extern void except_push(except_frame *fp);
extern void except_pop(void);
#endif
#ifdef WIN32
#define RAISE(e) except_raise(&(e), __FILE__, __LINE__)
#define RERAISE except_raise(except_frame.exception, \
            except_frame.file, except_frame.line)
#define RETURN switch (except_pop(), 0) default: return
#define TRY do { \
    volatile int except_flag; \
    except_frame except_frame; \
    if (except_index == -1) {  \
        except_init();        \
    }                     \
    except_push(&except_frame); \
    except_flag = setjmp(except_frame.env); \
    if (except_flag == except_entered) {
#define EXCEPT(e) \
        if (except_flag == except_entered) except_pop(); \
    } else if (except_frame.exception == &(e)) { \
        except_flag = except_handled;
#define ELSE   \
        if (except_flag == except_entered) except_pop(); \
    } else { \
        except_flag = except_handled;
#define FINALLY \
        if (except_flag == except_entered) except_pop(); \
    } { \
        if (except_flag == except_entered) \
            except_flag = except_finalized;
#define END_TRY \
        if (except_flag == except_entered) except_pop(); \
        } if (except_flag == except_raised) RERAISE;\
} while (0)
#else
#define RAISE(e) except_raise(&(e), __FILE__, __LINE__)
#define RERAISE except_raise(except_frame.exception, \
            except_frame.file, except_frame.line)
#define RETURN switch (except_stack == except_stack->prev, 0) default: return
#define TRY do { \
    volatile int except_flag; \
    except_frame except_frame; \
    except_frame.prev = except_stack; \
    except_stack = &except_frame; \
    except_flag = setjmp(except_frame.env); \
    if (except_flag == except_entered) {
#define EXCEPT(e)  \
        if (except_flag == except_entered) except_stack = except_stack->prev; \
    } else if (except_frame.exception == &(e)) { \
        except_flag = except_handled;
#define ELSE \
        if (except_flag == except_entered) except_stack = except_stack->prev; \
    } else { \
        except_flag = except_handled;
#define FINALLY \
        if (except_flag == except_entered) except_stack = except_stack->prev; \
    } { \
        if (except_flag == except_entered) \
            except_flag = except_finalized;
#define END_TRY \
        if (except_flag == except_entered) except_stack = except_stack->prev; \
    } if (except_flag == except_raised) RERAISE;\
} while (0)
#endif
#endif
